/* Copyright (C) 
* 2014 - ZengYajian
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* 
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
* 
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
* 
*/

#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/jiffies.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/export.h>
#include <linux/fs.h>
#include <linux/debugfs.h>
#include <linux/mutex.h>
#include <linux/err.h>
#include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/module.h>

static DECLARE_WAIT_QUEUE_HEAD(wait_queue);

static uint32_t wait_pending;
static char event;

static int wait_event_open(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t wait_event_read(struct file *filp, char __user *buffer,
		size_t count, loff_t *ppos)
{
	if (*ppos>=sizeof(wait_pending))
		return 0;

	wait_event_interruptible(wait_queue, wait_pending);
	if (copy_to_user(buffer, &wait_pending, sizeof(wait_pending)))
		return -EFAULT;

	*ppos += sizeof(wait_pending);

	wait_pending = 0;
	return sizeof(wait_pending);
}

static int test_event_open(struct inode *inode, struct file *file)
{
	return 0;
}

static ssize_t test_event_write(struct file *filp, const char __user *buffer,
		size_t count, loff_t *ppos)
{
	if (*ppos>=sizeof(event))
		return 0;

	if (copy_from_user(&event, buffer, sizeof(event)))
		return -EFAULT;

	*ppos += sizeof(event);

	pr_err("%s: recv '%c'\n", __func__, event);

	if (event=='w') {
		wait_pending = 'w';
		wake_up(&wait_queue);
	}

	return count;
}

static const struct file_operations wait_event_fops = {
	.owner = THIS_MODULE,
	.open  = wait_event_open,
	.read  = wait_event_read,
};

static const struct file_operations test_event_fops = {
	.owner = THIS_MODULE,
	.open  = test_event_open,
	.write = test_event_write,
};

struct dentry *dentry;
struct dentry *test_dentry;

static int __init event_wait_init(void)
{
	pr_err("%s\n", __func__);
	dentry = debugfs_create_file("wait_event", S_IRUGO, NULL, NULL,
				&wait_event_fops);

	test_dentry = debugfs_create_file("test_event", S_IRUGO, NULL, NULL,
				&test_event_fops);


	wait_pending = 0;

	return 0;
}

static void __exit event_wait_exit(void)
{
	pr_err("%s\n", __func__);
	debugfs_remove(dentry);
	debugfs_remove(test_dentry);
}

module_init(event_wait_init);
module_exit(event_wait_exit);

MODULE_LICENSE("GPL");

